import { compileTemplate } from '../compiler'
import { effect } from '../reactive/effect'
import { h } from '../runtime-core/h'
import {
  createElement,
  createText,
  insertAfterElement,
  insertElement,
  moveAfterElement,
  moveBeforeElement,
  querySelector,
  removeAttribute,
  removeElement,
  setAttribute,
  setText
} from '../runtime-dom'
import { forEach, isObject, isString } from '../share'

function createCompile(options) {
  const { template } = options
  const ast = compileTemplate(template)
  return ast
}

export function createMount(sel) {
  let oldVnode = null
  const container = querySelector(sel)
  oldVnode = effect(() => {
    // 获得ast语法树
    const { tag, props, children } = createCompile(this.$options)
    // 调用h函数转换成vnode
    const vnode = h(tag, props, children)
    console.log(vnode)
    // 替换变量为data中数据
    updateComponent.bind(this)(vnode)
    // diff比较新旧树
    const patchVnode = patch.bind(this)(container, vnode, oldVnode)
    oldVnode = patchVnode
  })
}

function updateComponent(vnode) {
  const that = this
  // 静态节点标记，后面不渲染
  let isStaticProps = true,
    isStaticChildren = true
  forEach(vnode.props, function (value, key) {
    if (isObject(value)) {
      // 属性替换
      if (value.directive) {
        vnode.props[key] = that[value.value]
        isStaticProps = false
      }
      // 事件处理
      else if (value.event) {
        isStaticProps = false
      }
      // 文本表达式
      else if (value.express) {
        vnode.text = that[value.value]
        delete vnode.props[key]
        isStaticProps = false
      }
    }
  })
  forEach(vnode.children, function (value) {
    const isStatic = updateComponent.bind(that)(value)
    if (!isStatic) {
      isStaticChildren = isStatic
    }
  })
  // 静态节点标记，后面diff
  vnode.isStaticProps = isStaticProps
  vnode.isStaticChildren = isStaticChildren
  return isStaticProps && isStaticChildren
}

function sameNode(node1, node2) {
  return node1.tag == node2.tag && node1.key == node2.key
}

function patch(container, vnode, oldVnode) {
  if (!oldVnode) {
    // 第一次渲染
    // 渲染vnode树到页面
    render.bind(this)(container, vnode)
    return vnode
  } else {
    // 静态节点跳过
    if (vnode.isStaticProps && vnode.isStaticChildren) return
    // 1、老文本，新文本 直接替换
    if (
      !oldVnode.tag &&
      !vnode.tag &&
      isString(oldVnode.text) &&
      isString(vnode.text)
    ) {
      if (oldVnode.text != vnode.text) {
        oldVnode.text = vnode.text
        setText(oldVnode.el, vnode.text)
      }
    }
    // 2、老文本，新不是文本
    else if (!oldVnode.tag && isString(oldVnode.text) && vnode.tag) {
      const newEl = render(null, vnode)
      replaceElement(oldVnode.el, newEl)
    }
    // 3、老不是文本，新文本
    else if (!vnode.tag && isString(vnode.text) && oldVnode.tag) {
      const newEl = render(null, vnode)
      replaceElement(oldVnode.el, newEl)
    }
    // 4、老的不是文本，新的不是文本，diff算法
    else if (vnode.tag && oldVnode.tag) {
      let newStartIndex = 0,
        oldStartIndex = 0,
        newEndIndex = vnode.children.length - 1,
        oldEndIndex = oldVnode.children.length - 1
      while (newStartIndex < newEndIndex && oldStartIndex < oldEndIndex) {
        let newStartNode = vnode.children[newStartIndex],
          newEndNode = vnode.children[newEndIndex],
          oldStartNode = oldVnode.children[oldStartIndex],
          oldEndNode = oldVnode.children[oldEndIndex]
        // 新前 - 旧前，不用移动位置
        if (sameNode(newStartNode, oldStartNode)) {
          patchAttrs(newStartNode, oldStartNode)
          newStartIndex++
          oldStartIndex++
        }
        // 新后 - 旧后，不用移动位置
        else if (sameNode(newEndNode, oldEndNode)) {
          patchAttrs(newEndNode, oldEndNode)
          newEndIndex--
          oldEndIndex--
        }
        // 新后 - 旧前，旧前移到旧后之后
        else if (sameNode(newEndNode, oldStartNode)) {
          patchAttrs(newEndNode, oldStartNode)
          moveAfterElement(oldStartNode, oldEndNode)
          newEndIndex--
          oldStartIndex++
        }
        // 新前 - 旧后，旧后移动到新前之前
        else if (sameNode(newStartNode, oldEndNode)) {
          patchAttrs(newStartNode, oldEndNode)
          moveBeforeElement(oldEndNode, newStartNode)
          newStartIndex++
          oldEndIndex--
        }
      }
      // 新节点还有剩余，都插入到旧前之后
      if (newStartIndex < newEndIndex) {
        for (; newStartIndex < newEndIndex; newStartIndex++) {
          insertAfterElement(vnode[newStartIndex], oldVnode[oldStartIndex])
        }
      }
      // 旧节点还有剩余，全部删除
      else if (oldStartIndex < oldEndIndex) {
        for (; oldStartIndex < oldEndIndex; oldStartIndex++) {
          removeElement(oldVnode.children[oldStartIndex])
        }
      }
      // 新建节点，插入到旧前之前
    }
  }
  // 递归比较字节的
  forEach(oldVnode.children, (child, index) => {
    patch(child.el, vnode.children[index], child)
  })
}

function patchAttrs(vnode, oldVnode) {
  // 移除已经不存在的属性
  forEach(oldVnode.props, (value, key) => {
    if (!vnode.props[key]) {
      removeAttribute(oldVnode.el, key)
    }
  })
  // 添加新属性
  forEach(vnode.props, (value, key) => {
    if (vnode.props[key] != oldVnode.props[key]) {
      setAttribute(vnode.el, key, value)
    }
  })
}

function render(container, vnode) {
  let el = null
  if (vnode.tag) {
    el = createElement(vnode.tag)
  } else if (vnode.text) {
    el = createText(vnode.text)
  }
  vnode.el = el
  forEach(vnode.props, (prop, key) => {
    setAttribute(el, key, prop)
  })
  forEach(vnode.children, child => {
    render(el, child)
  })
  if (container) {
    insertElement(container, el)
  } else {
    return el
  }
}
